iT邦幫忙

2022 iThome 鐵人賽

DAY 24
0
Modern Web

拾起 Canvas,人人都是大藝術家!系列 第 24

第 24 幅 - 以 Matter.js 實作地心引力效果,讓你的動畫完美落地

  • 分享至 

  • xImage
  •  

嗨嗨!插播了復刻小遊戲後,讓我們繼續回到 Canvas Library 的世界吧,今天要分享的是 Matter.js,也就是之前分享過可以用來操作並模擬物理反應的 js Library。做一個地板,並讓幾顆球落地時彈跳的狀態不一樣就是我們今天的小目標。

地心引力小球

1. 起手式與 useRef 設定

一樣先安裝起來~

npm install matter-js

因為此專案是以 React 作為框架,所以不以 getelement 的方式操作 DOM,我們首先要先給主要的 Canvas 元素和包著 Canvas 的 wrapper 作用區域以 useRef() 選起來,幫助我們透過操作 Component 的概念操作 DOM。

因為是以 Typescript 作為開發語言,我在設定 useRef() 時一直報錯,後來是加上 React.MutableRefObject<HTMLDivElement> 以及 React.MutableRefObject<HTMLCanvasElement> 的型別才解決的,想了解為什麼這裡要設定 MutableRefObject 這個型別的可以再深入了解,這裡就不特別說明了,設定完 ref 後把 return 的 JSX 也先寫好,就可以開始設定相關物件參數。

import React, { useEffect, useRef } from 'react';
import Matter from 'matter-js';

const wrapperRef = useRef() as React.MutableRefObject<HTMLDivElement>;
const canvasRef = useRef() as React.MutableRefObject<HTMLCanvasElement>;


return (
        <div
            ref={wrapperRef}
            style={{
                width: 300,
                height: 300,
            }}>
            <canvas ref={canvasRef} />
        </div>
    );

2. Main module APIs

列出來幾個 Main module APIs,也就是我們所有可以調用與設定參數的 API

  • Engine :模擬引擎, Trigger 所有 body 物件 與 world 之間的互動效果
  • Render :Canvas 渲染
  • Runner :讓模擬引擎成為一個 loop,讓效果持續循環
  • Bodies :繪製出主要的 body 物件,factory methods 可以選擇一些圖形,例如矩形、圓形、多邊形,也可以設定物件的基本參數
  • Composite :Bodies 模組,創建多個 body 物件組合而成的物件
  • Constraint:兩個物體之間的物理限制,例如物件之間距離、彈性等等,限制是兩個物體之間需維持固定距離
  • MouseConstraint:滑鼠行為,這個 API 讓我們可以透過滑鼠或是觸控去控制物件,拋丟等操作
  • World:代表整個模擬世界,可以在這個世界創造重力、地板邊界等屬性

下方程式碼為例,我們調用了 Engine、Render、World、Bodies 等四個 API,並在 render 中設定一個渲染畫面,裡面包含整個畫面的背景色、使用的 canvas 與 element 。

useEffect(() => {
        // module aliases
        const Engine = Matter.Engine;
        const Render = Matter.Render;
        const World = Matter.World;
        const Bodies = Matter.Bodies;

        // create an engine
        const engine = Engine.create();

        // create a renderer
        const render = Render.create({
            element: wrapperRef.current,
            engine: engine,
            canvas: canvasRef.current,
            options: {
                width: 300,
                height: 300,
                background: '#F5F5F5',
                wireframes: false,
            },
        });

        ...

    }, []);

3. 創造 Bodies 以及 World

接著我們創造了一個地板,地板的位置以及長寬都是用 rectangle(x 坐標, y 坐標, 長度 , 寬度) 的原理創造出來,只是這裡多了一個沒見過的參數 isStatic, 這個參數代表固定的,也就是該物件是不會移動的

接著創造了三個圓形,circle (x 坐標, y 坐標, 半徑 ),因為我們希望這個圓有掉下來的效果,因此這邊的 y 坐標都給了 0 ,這樣就會從高處掉落。比較特別的是用到了 restitution 這個參數的意思其實就是掉落或是碰撞後彈跳起來的程度,0 表示發生碰撞後完全不彈跳,1 彈跳的最大程度。

friction 的話則為「阻力」,阻力越大掉落的越緩滿,這裡很特別的是也另外有空氣阻力的參數可以設定,這些參數都可以幫我們模擬更真實世界的環境,大家可以自己調整參數看看效果。

最後,我們只要給 World 世界加上這些 Bodies,並讓引擎與畫面被啟動 run() 即可完成今天的畫面囉!

useEffect(() => {
        
				...

        // create three balls and a ground
        const floor = Bodies.rectangle(150, 300, 300, 20, {
            isStatic: true,
            render: {
                fillStyle: 'blue',
            },
        });

        const greenBall = Bodies.circle(150, 0, 10, {
            restitution: 0.9,
            friction: 0.1,
            render: {
                fillStyle: 'green',
            },
        });

        const redBall = Bodies.circle(150, 0, 10, {
            restitution: 0.5,
            friction: 0.5,
            render: {
                fillStyle: 'red',
            },
        });

        const yellowBall = Bodies.circle(100, 0, 10, {
            restitution: 0.1,
            render: {
                fillStyle: 'orange',
            },
        });

        World.add(engine.world, [greenBall, redBall, yellowBall, floor]);
        Engine.run(engine);
        Render.run(render);

    }, []);

以上就是今天的初入 Matter.js,其實他還有很多屬性可以玩,就交給大家去摸索囉!

參考文章:https://blog.arvinh.info/tech/matterjs-intro
程式碼參考:https://paulie.dev/posts/2020/08/react-hooks-and-matter-js/
官方網站 APIs :https://brm.io/matter-js/docs/


上一篇
第 23 幅 - 用 Canvas 復刻 Pokemon ,做個 RPG 小遊戲吧!(下)
下一篇
第 25 幅 - 用 Matter.js 讓物件彼此有一點點互動 ~
系列文
拾起 Canvas,人人都是大藝術家!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言